package org.starsource.forum.server.dao.test;

import static org.junit.Assert.*;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Date;
import java.util.Random;
import java.util.Set;
import java.sql.Timestamp;

import org.junit.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.starsource.forum.client.exception.DataNotFoundException;
import org.starsource.forum.client.exception.DatabaseException;
import org.starsource.forum.server.dao.CategoryDao;
import org.starsource.forum.server.dao.SessionDao;
import org.starsource.forum.server.dao.UserDao;
import org.starsource.forum.server.domain.Category;
import org.starsource.forum.server.domain.Session;
import org.starsource.forum.server.domain.User;

public abstract class AbsTestSessionDao {

//	private static org.apache.log4j.Logger logger = org.apache.log4j.LogManager
//	.getLogger(AbsTestSessionDao.class.getName());

	@Autowired
	private UserDao userDao;

	@Autowired
	private CategoryDao categoryDao;

	@Autowired
	private SessionDao sessionDao;

	@Test
	public void testSave() throws DatabaseException {
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Timestamp createTimestamp, updateTimestamp;
		createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			fail("Test fail because thread sleep");
		}
		updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		Session session1 = new Session(user1, "TITLE_FOR_TEST", category1, createTimestamp, updateTimestamp);
		sessionDao.save(session1);

		Session session2 = sessionDao.getSession(session1.getId());

		assertEquals(session1, session2);

		Session session3 = new Session();
		session3.setOwner(user1);
		session3.setCategory(category1);
		sessionDao.save(session3);

		Session session4 = sessionDao.getSession(session3.getId());

		assertEquals(session3, session4);
	}

	@Test
	public void testUpdate() throws DatabaseException {
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);
		User user2 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user2);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Timestamp createTimestamp, updateTimestamp;
		createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		Session session1 = new Session(user1, "TITLE_FOR_TEST", category1, createTimestamp, updateTimestamp);
		sessionDao.save(session1);

		int sessionId = session1.getId();

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			fail("Test fail because thread sleep");
		}
		createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			fail("Test fail because thread sleep");
		}
		updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);

		session1.setOwner(user2);
		session1.setTitle("TITLE_FOR_UNIT_TEST_NEW");
		session1.setUpdateTimestamp(updateTimestamp);
		session1.setCreateTimestamp(createTimestamp);

		sessionDao.update(session1);

		Session session2 = sessionDao.getSession(sessionId);
		assertEquals(session1, session2);

//		session1.setOwner(null);
		session1.setTitle(null);
		session1.setUpdateTimestamp(null);
		session1.setCreateTimestamp(null);

		sessionDao.update(session1);

		Session session3 = sessionDao.getSession(sessionId);
		assertEquals(session1, session3);
	}

	@Test
	public void testDelete() throws DatabaseException {
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Timestamp createTimestamp, updateTimestamp;
		createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			fail("Test fail because thread sleep");
		}
		updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		Session session1 = new Session(user1, "TITLE_FOR_TEST", category1, createTimestamp, updateTimestamp);
		sessionDao.save(session1);
		int sessionId = session1.getId();
		sessionDao.delete(session1);

		try {
			sessionDao.getSession(sessionId);
			assertTrue(false);
		} catch (DataNotFoundException e) {
		}
	}

	@Test
	public void testGetSession() throws DatabaseException {
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Timestamp createTimestamp, updateTimestamp;
		createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			fail("Test fail because thread sleep");
		}
		updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
		Session session1 = new Session(user1, "TITLE_FOR_TEST", category1, createTimestamp, updateTimestamp);
		sessionDao.save(session1);

		Session session2 = sessionDao.getSession(session1.getId());

		assertEquals(session1, session2);
	}

	@Test
	public void testGetSessionSet() throws DatabaseException {
		Random random = new Random();
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		int round = 10;
		Set<Session> sessionSet1 = new LinkedHashSet<Session>();

		Timestamp createTimestamp, updateTimestamp;

		for(int j=0; j<round; j++) {
			createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				fail("Test fail because thread sleep");
			}
			updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			Session session = new Session(user1, "TITLE_" + random.nextInt(), category1, createTimestamp, updateTimestamp);
			session.setCategory(category1);
			sessionDao.save(session);

			sessionSet1.add(session);
		}

		Set<Session> sessionSet2 = sessionDao.getSessionSet(category1);

//		Iterator<Session> iterator;
//		logger.debug("checking sessionSet1..");
//		iterator = sessionSet1.iterator();
//		while (iterator.hasNext()) {
//			Session session = iterator.next();
//			logger.debug("SESSION: ID=" + session.getId() + " TITLE=" + session.getTitle()
//					+ " CATEGORY_ID=" + session.getCategoryId() + " OWNER_ID=" + session.getOwnerId()
//					+ " CREATE_TIMESTAMP=" + session.getCreateTimestamp()
//					+ " UPDATE_TIMESTAMP=" + session.getUpdateTimestamp());
//		}
//
//		logger.debug("checking sessionSet2..");
//		iterator = sessionSet2.iterator();
//		while (iterator.hasNext()) {
//			Session session = iterator.next();
//			logger.debug("SESSION: ID=" + session.getId() + " TITLE=" + session.getTitle()
//					+ " CATEGORY_ID=" + session.getCategoryId() + " OWNER_ID=" + session.getOwnerId()
//					+ " CREATE_TIMESTAMP=" + session.getCreateTimestamp()
//					+ " UPDATE_TIMESTAMP=" + session.getUpdateTimestamp());
//		}

		assertTrue(sessionSet2.size()>=round);
		Iterator<Session> iterator1 = sessionSet1.iterator();
		while (iterator1.hasNext()) {
			Session session1 = iterator1.next();
			Iterator<Session> iterator2 = sessionSet2.iterator();
			boolean found = false;
			while (iterator2.hasNext()) {
				if (iterator2.next().equals(session1)) {
					found = true;
				}
			}
			assertTrue(found);
		}
//		assertTrue(sessionSet2.containsAll(sessionSet1));

	}

	@Test
	public void testGetSessionSetCategories() throws DatabaseException {
		Random random = new Random();
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Category category2 = new Category(category1, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		category1.getChildren().add(category2);
		categoryDao.save(category2);

		Category category3 = new Category(category1, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		category1.getChildren().add(category3);
		categoryDao.save(category3);

		int round = 10;
		Set<Session> sessionSet1 = new LinkedHashSet<Session>();

		Timestamp createTimestamp, updateTimestamp;

		for(int j=0; j<round; j++) {
			createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				fail("Test fail because thread sleep");
			}
			updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			Session session = null;
			switch (j % 2) {
			case 0: session = new Session(user1, "TITLE_" + random.nextInt(), category2, createTimestamp, updateTimestamp);
			case 1: session = new Session(user1, "TITLE_" + random.nextInt(), category3, createTimestamp, updateTimestamp);
//			case 2: session = new Session(user1, "TITLE_" + random.nextInt(), category1, createTimestamp, updateTimestamp);
			}
			sessionDao.save(session);

			sessionSet1.add(session);
		}

		Set<Session> sessionSet2 = sessionDao.getSessionSet(category1.getChildren());
		assertTrue(sessionSet2.size()>=round);
		Iterator<Session> iterator1 = sessionSet1.iterator();
		while (iterator1.hasNext()) {
			Session session1 = iterator1.next();
			Iterator<Session> iterator2 = sessionSet2.iterator();
			boolean found = false;
			while (iterator2.hasNext()) {
				if (iterator2.next().equals(session1)) {
					found = true;
				}
			}
			assertTrue(found);
		}
//		assertTrue((new HashSet<Session>(sessionSet2)).containsAll(new HashSet<Session>(sessionSet1)));
//		assertTrue(sessionSet2.containsAll(sessionSet1));
	}

	@Test
	public void testgetSessionSetByKeywords() throws DatabaseException {
		Random random = new Random();
		User user1 = new User("ONLY_FOR_UNIT_TEST", "PASSWORD_FOR_TEST", "NAME_FOR_TEST", "DIRECTORY_FOR_TEST");
		userDao.save(user1);

		Category category1 = new Category(null, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		categoryDao.save(category1);

		Category category2 = new Category(category1, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		category1.getChildren().add(category2);
		categoryDao.save(category2);

		Category category3 = new Category(category1, "NAME_FOR_UNIT_TEST", "CAPTION_FOR_UNIT_TEST");
		category1.getChildren().add(category3);
		categoryDao.save(category3);

		int round = 10;
		Set<Session> sessionSet1 = new LinkedHashSet<Session>();

		Timestamp createTimestamp, updateTimestamp;

		String tag = "TAG_" + random.nextInt() + "_" + random.nextInt();

		for(int j=0; j<round; j++) {
			createTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				fail("Test fail because thread sleep");
			}
			updateTimestamp = new Timestamp(new Date().getTime()/1000*1000);
			Session session = null;
			switch (j % 2) {
			case 0: session = new Session(user1, tag + "_TITLE_SEARCH_TEST_测试_" + random.nextInt(), category2, createTimestamp, updateTimestamp);
			case 1: session = new Session(user1, tag + "_TITLE_SEARCH_TEST_测试_" + random.nextInt(), category3, createTimestamp, updateTimestamp);
//			case 2: session = new Session(user1, "TITLE_" + random.nextInt(), category1, createTimestamp, updateTimestamp);
			}
			sessionDao.save(session);

			sessionSet1.add(session);
		}

		Set<Session> sessionSet2 = sessionDao.getSessionSetByKeywords(new String[] {});
		assertTrue(sessionSet2.size() >= round);

		Set<Session> sessionSet3 = sessionDao.getSessionSetByKeywords(new String[] {tag});
		assertTrue(sessionSet3.size() == round);

		Set<Session> sessionSet4 = sessionDao.getSessionSetByKeywords(new String[] {"测试"});
		assertTrue(sessionSet4.size() >= round);

		Set<Session> sessionSet5 = sessionDao.getSessionSetByKeywords(new String[] {tag,"TEST","测试"});
		assertTrue(sessionSet5.size() >= round);

		Iterator<Session> iterator1 = sessionSet1.iterator();
		while (iterator1.hasNext()) {
			Session session1 = iterator1.next();
			Iterator<Session> iterator2 = sessionSet5.iterator();
			boolean found = false;
			while (iterator2.hasNext()) {
				if (iterator2.next().equals(session1)) {
					found = true;
				}
			}
			assertTrue(found);
		}

		Set<Session> sessionSet6 = sessionDao.getSessionSetByKeywords(new String[] {tag + "FOOBAR"});
		assertTrue(sessionSet6.size() == 0);

		//		assertTrue((new HashSet<Session>(sessionSet2)).containsAll(new HashSet<Session>(sessionSet1)));
//		assertTrue(sessionSet2.containsAll(sessionSet1));
	}
}
